<template>
  <div id="antvX6">
    <!-- 自定义右键菜单 -->
    <context-menu
      :visible.sync="contextMenuVisible"
      :x="contextMenuX"
      :y="contextMenuY"
      :canCopy="contextMenuOptions.canCopy"
      :canPaste="contextMenuOptions.canPaste"
      :canDelete="contextMenuOptions.canDelete"
      :canViewResult="contextMenuOptions.canViewResult"
      @copy="handleCopy"
      @paste="handlePaste"
      @delete="handleDelete"
      @viewResult="handleViewResult"
    />
    <!-- 头部 -->
    <div class="content-header">
      <div
        style="
          height: 100%;
          display: flex;
          align-items: center;
          justify-content: space-between;
        "
      >
        <div style="display: flex; align-items: center">
          <div class="test" style="color: #ffffff; font-size: 15px">
            实验进度
          </div>
          <div style="width: 250px; margin-left: 10px">
            <el-progress :percentage="50"></el-progress>
          </div>
        </div>
        <div class="time-count">
          <div style="height: 100%">
            {{ $route.query.traExperName }}
          </div>
        </div>
        <div>
          <!-- <timeCount /> -->
        </div>
      </div>
    </div>
    <div class="content-main">
      <!-- 左侧 -->
      <div class="content-left">
        <div class="header">实验指导</div>
        <div class="mask-left"></div>
      </div>
      <div class="resize"></div>
      <!-- 右侧 -->
      <div class="content-right">
        <div class="antv-menu">
          <!-- <el-button
        type="primary"
        @click="save"
        style="position: absolute; bottom: 0; left: 0"
        >保存</el-button
      > -->
          <div class="category" v-for="itemC in menuList" :key="itemC.name">
            <div class="title">
              <div class="left">
                <i :class="itemC.icon"></i
                ><span style="margin-left: 5px">{{ itemC.name }}</span>
              </div>
              <div class="right" @click="collapse(itemC)">
                <i
                  :class="
                    itemC.isExpand ? 'el-icon-arrow-down' : 'el-icon-arrow-up'
                  "
                ></i>
              </div>
            </div>
            <el-collapse-transition>
              <div class="cates-content" v-show="itemC.isExpand">
                <div
                  class="cate"
                  draggable="true"
                  v-for="item in itemC.menuItems"
                  :key="item.name"
                  @mousedown="menuDrag(item, $event)"
                >
                  <div
                    class="input"
                    v-show="item.type == 'all' || item.type == 'input'"
                  ></div>
                  <div
                    class="output"
                    v-show="item.type == 'all' || item.type == 'output'"
                  ></div>
                  <div class="icon">
                    <i :class="item.icon"></i>
                  </div>
                  <div class="name">{{ item.name }}</div>
                </div>
              </div>
            </el-collapse-transition>
          </div>
        </div>
        <div class="antv-canvas" id="antv"></div>
        <!-- 结果抽屉 -->
        <div class="result-drawer" v-show="drawerVisible">
          <div class="drawer-header">
            <span>运行结果</span>
            <i class="el-icon-close" @click="closeDrawer"></i>
          </div>
          <div class="drawer-resize-handle" @mousedown="handleDrawerResize"></div>
          <div class="drawer-content">
            <div v-if="resultData">
              <pre>{{ resultData }}</pre>
            </div>
            <div v-else class="no-result">
              暂无运行结果
            </div>
          </div>
        </div>
        <div class="mask-right"></div>
      </div>
    </div>
  </div>
</template>

<script>
import CustomNode from "./CustomNode.vue";
import { Graph, Path } from "@antv/x6";
import { register } from "@antv/x6-vue-shape";
import { Dnd } from "@antv/x6-plugin-dnd";
import { Selection } from "@antv/x6-plugin-selection";
import ContextMenu from "../components/ContextMenu.vue";
export default {
  components: {
    ContextMenu,
  },
  data() {
    return {
      // 右键菜单相关数据
      contextMenuVisible: false,
      contextMenuX: 0,
      contextMenuY: 0,
      contextMenuOptions: {
        canCopy: true,
        canPaste: true,
        canDelete: true,
        canViewResult: true,
      },
      // 当前选中的节点
      selectedNode: null,
      // 复制的节点数据
      copiedNodeData: null,
      // 抽屉相关数据
      drawerVisible: false,
      drawerHeight: 200,
      resultData: null,
      initialY: 0,
      initialHeight: 0,
      menuList: [
        {
          name: "数据读取",
          icon: "el-icon-question",
          isExpand: true,
          menuItems: [
            {
              name: "数据库",
              icon: "el-icon-platform-eleme",
              type: "input",
              inputNum: 2,
            },
            {
              name: "文件",
              icon: "el-icon-document",
              type: "input",
              inputNum: 3,
            },
            {
              name: "API",
              icon: "el-icon-phone",
              type: "all",
              inputNum: 1,
            },
            {
              name: "表单",
              icon: "el-icon-info",
              type: "output",
            },
          ],
        },
      ],
      graph: null,
    };
  },

  methods: {
    // 注册插件
    handleUsePlugin() {
      this.graph.use(
        //点选/框选,开启后可以通过点击或者套索框选节点
        new Selection({
          className: "node-selected", //选中节点的class
          enabled: true, //启用
          rubberband: false, //是否启用框选
          showNodeSelectionBox: true, //是否显示节点的选择框
        })
      );
    },
    initGraph() {
      register({
        shape: "custom-node",
        width: 300,
        height: 180,
        component: CustomNode,
        ports: {
          groups: {
            input: {
              position: "left",
              attrs: {
                circle: {
                  r: 6,
                  magnet: true,
                  stroke: "#5F95FF",
                  strokeWidth: 2,
                  fill: "#fff",
                },
              },
            },
            output: {
              position: "right",
              attrs: {
                circle: {
                  r: 6,
                  magnet: true,
                  stroke: "#5F95FF",
                  strokeWidth: 2,
                  fill: "#fff",
                },
              },
            },
          },
        },
      });
      Graph.registerConnector(
        "curveConnector",
        (sourcePoint, targetPoint) => {
          const hgap = Math.abs(targetPoint.x - sourcePoint.x);
          const path = new Path();
          path.appendSegment(
            Path.createSegment("M", sourcePoint.x + 6, sourcePoint.y)
          );
          path.appendSegment(
            Path.createSegment("L", sourcePoint.x + 12, sourcePoint.y)
          );
          // 水平三阶贝塞尔曲线
          path.appendSegment(
            Path.createSegment(
              "C",
              sourcePoint.x < targetPoint.x
                ? sourcePoint.x + hgap / 2
                : sourcePoint.x - hgap / 2,
              sourcePoint.y,
              sourcePoint.x < targetPoint.x
                ? targetPoint.x - hgap / 2
                : targetPoint.x + hgap / 2,
              targetPoint.y,
              targetPoint.x - 6,
              targetPoint.y
            )
          );
          path.appendSegment(
            Path.createSegment("L", targetPoint.x - 6, targetPoint.y)
          );

          return path.serialize();
        },
        true
      );
      this.graph = new Graph({
        container: document.getElementById("antv"),
        panning: true,
        autoResize: true,
        mousewheel: {
          enabled: true,
          modifiers: "ctrl",
          factor: 1.1,
          maxScale: 1.5,
          minScale: 0.8,
        },
        background: {
          color: "#F5F7FA",
        },
        grid: {
          size: 10,
          visible: true,
        },
        // 配置连接线样式
        connecting: {
          router: "manhattan",
          connector: {
            name: "curveConnector",
          },
          anchor: "center",
          connectionPoint: "anchor",
          allowBlank: false,
          highlight: true,
          snap: {
            radius: 20,
          },
          validateMagnet({ magnet }) {
            // return magnet.getAttribute('magnet') === 'true'
            return !magnet.getAttribute("data-port-id").includes("input");
          },
          createEdge() {
            return this.createEdge({
              attrs: {
                line: {
                  stroke: "#5F95FF",
                  strokeWidth: 1,
                  targetMarker: {
                    name: "classic",
                    size: 8,
                  },
                },
              },
            });
          },
          // 连接规则验证
          validateConnection({
            sourceView,
            targetView,
            sourceMagnet,
            targetMagnet,
          }) {
            if (!sourceMagnet || !targetMagnet) {
              return false;
            }

            // 禁止自连接
            if (sourceView === targetView) {
              return false;
            }

            // 获取连接桩信息
            const sourcePortId = sourceMagnet.getAttribute("data-port-id");
            const targetPortId = targetMagnet.getAttribute("data-port-id");

            if (!sourcePortId || !targetPortId) {
              return false;
            }

            // 只允许 output -> input 连接
            const sourcePortType = sourcePortId.split("-")[0];
            const targetPortType = targetPortId.split("-")[0];

            if (sourcePortType !== "output" || targetPortType !== "input") {
              return false;
            }

            // 不再添加port-connected类

            // 检查目标输入端口是否已经连接（input端口只能连接一次）
            const targetNode = targetView.cell;
            const edges = this.getConnectedEdges(targetNode);

            for (const edge of edges) {
              const { port: targetPort } = edge.getTerminal("target");
              if (targetPort === targetPortId) {
                return false; // 该输入端口已经连接
              }
            }

            return true;
          },
        },
      });

      this.dnd = new Dnd({
        target: this.graph,
      });
      this.handleUsePlugin();
      // 监听边的添加事件
      this.graph.on("edge:connected", ({ edge }) => {
        // 设置边的样式
        edge.attr({
          line: {
            stroke: "#5F95FF",
            strokeWidth: 2,
            targetMarker: {
              name: "classic",
              size: 8,
            },
            strokeDasharray: 0,
            style: {
              animation: "none",
            },
          },
        });

        // 确保连接桩状态更新
        // const { source, target } = edge.getTerminal()
        const source = edge.getSource();
        const target = edge.getTarget();
        const sourceNode = this.graph.getCellById(source.cell);
        const targetNode = this.graph.getCellById(target.cell);

        if (sourceNode && targetNode) {
          // 触发节点更新
          sourceNode.trigger("edge:connected", { edge });
          targetNode.trigger("edge:connected", { edge });
        }
      });

      // 监听节点右键事件
      this.graph.on("node:contextmenu", ({ e, node }) => {
        e.preventDefault();
        this.selectedNode = node;
        this.showContextMenu(e, true);
      });

      // 监听画布右键事件
      this.graph.on("blank:contextmenu", ({ e }) => {
        e.preventDefault();
        this.selectedNode = null;
        this.showContextMenu(e, false);
      });

      // 监听节点点击事件，用于选中节点
      this.graph.on("node:click", ({ node }) => {
        this.selectedNode = node;
      });

      this.graph.on("node:mousedown", (e) => {});

      // 添加示例节点
      this.graph.addNode({
        id: "custom-node1",
        shape: "custom-node", // 使用注册的类型
        x: 100,
        y: 40,
        data: { label: "自定义节点", value: "el-icon-warning", id: "output" }, // 传递给组件的数据
      });
    },
    menuDrag(row, event) {
      // 创建节点
      const node = this.graph.createNode({
        id: "custom-node1",
        shape: "custom-node", // 使用注册的类型
        x: 100,
        y: 40,
        data: {
          label: row.name,
          value: row.icon,
          id: row.type,
          inputNum: row.inputNum || 0, // 传递输入连接桩数量
        }, // 传递给组件的数据
      });

      this.dnd.start(node, event);
    },
    collapse(row) {
      row.isExpand = !row.isExpand;
    },
    save() {
      const { cells } = this.graph.toJSON();
      console.log(cells);
    },
    // 显示右键菜单
    showContextMenu(e, isNode) {
      // 设置菜单位置
      this.contextMenuX = e.clientX;
      this.contextMenuY = e.clientY;

      // 根据右击对象设置菜单项状态
      if (isNode) {
        // 右击节点时
        this.contextMenuOptions = {
          canCopy: true, // 可以复制
          canPaste: false, // 禁用粘贴
          canDelete: true, // 可以删除
          canViewResult: true, // 可以查看运行结果
        };
      } else {
        // 右击画布时
        this.contextMenuOptions = {
          canCopy: false, // 禁用复制
          canPaste: true, // 可以粘贴
          canDelete: false, // 禁用删除
          canViewResult: false, // 禁用查看运行结果
        };
      }

      // 显示菜单
      this.contextMenuVisible = true;
    },
    // 处理复制操作
    handleCopy() {
      if (this.selectedNode) {
        // 复制节点数据
        this.copiedNodeData = this.selectedNode.toJSON();
        console.log("节点已复制", this.copiedNodeData);
      }
    },
    // 处理粘贴操作
    handlePaste() {
      if (this.copiedNodeData) {
        // 创建新节点，使用复制的数据
        const newNode = this.graph.createNode({
          ...this.copiedNodeData,
          id: `node-${Date.now()}`, // 生成新的ID
          position: {
            x: this.contextMenuX - 100, // 在右键位置创建
            y: this.contextMenuY - 50,
          },
        });
        this.graph.addNode(newNode);
        console.log("节点已粘贴");
      }
    },
    // 处理删除操作
    handleDelete() {
      if (this.selectedNode) {
        this.graph.removeNode(this.selectedNode);
        this.selectedNode = null;
        console.log("节点已删除");
      }
    },
    // 处理查看运行结果操作
    handleViewResult() {
      if (this.selectedNode) {
        console.log("查看节点运行结果", this.selectedNode.id);
        // 显示抽屉
        this.drawerVisible = true;
        // 模拟获取节点运行结果数据
        this.resultData = JSON.stringify(
          {
            nodeId: this.selectedNode.id,
            nodeName: this.selectedNode.getData().label,
            status: "success",
            data: {
              timestamp: new Date().toLocaleString(),
              results: [1, 2, 3, 4, 5],
              summary: "运行成功"
            }
          }, 
          null, 
          2
        );
      }
    },
    
    // 关闭抽屉
    closeDrawer() {
      this.drawerVisible = false;
    },
    
    // 处理抽屉拉伸
    handleDrawerResize(e) {
      e.preventDefault();
      this.initialY = e.clientY;
      this.initialHeight = this.drawerHeight;
      
      const handleMouseMove = (moveEvent) => {
        const deltaY = this.initialY - moveEvent.clientY;
        const newHeight = Math.max(100, this.initialHeight + deltaY);
        this.drawerHeight = newHeight;
        document.querySelector('.result-drawer').style.height = `${newHeight}px`;
      };
      
      const handleMouseUp = () => {
        document.removeEventListener('mousemove', handleMouseMove);
        document.removeEventListener('mouseup', handleMouseUp);
      };
      
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
    },
    // 处理全局右键事件
    handleGlobalContextMenu(e) {
      // 只有在画布区域外才阻止默认右键菜单
      // 这样可以让图形区域的右键事件正常工作
      const antvCanvas = document.getElementById("antv");
      if (!antvCanvas.contains(e.target)) {
        e.preventDefault();
      }
    },
    //初始化 添加左右容器拉伸
    dragControllerDiv() {
      var resize = document.getElementsByClassName("resize");
      var left = document.getElementsByClassName("content-left");
      var mid = document.getElementsByClassName("content-right");
      var box = document.getElementsByClassName("content-main");
      var mask = document.getElementsByClassName("mask-right")[0];
      var mask1 = document.getElementsByClassName("mask-left")[0];
      for (let i = 0; i < resize.length; i++) {
        // 鼠标按下事件
        resize[i].onmousedown = function (e) {
          //颜色改变提醒
          resize[i].style.background = "#818181";
          var startX = e.clientX;
          console.log(startX, "w");
          resize[i].left = resize[i].offsetLeft;
          // 鼠标拖动事件
          document.onmousemove = function (e) {
            mask.style.display = "block";
            mask1.style.display = "block";
            var endX = e.clientX;
            var moveLen = resize[i].left + (endX - startX); // （endx-startx）=移动的距离。resize[i].left+移动的距离=左边区域最后的宽度
            var maxT = box[i].clientWidth - resize[i].offsetWidth; // 容器宽度 - 左边区域的宽度 = 右边区域的宽度
            if (moveLen < 32) moveLen = 32; // 左边区域的最小宽度为32px
            if (moveLen > maxT - 130) moveLen = maxT - 130; //右边区域最小宽度为150px
            resize[i].style.left = moveLen; // 设置左侧区域的宽度
            for (let j = 0; j < left.length; j++) {
              left[j].style.width = moveLen + "px";
              mid[j].style.width = box[i].clientWidth - moveLen - 10 + "px";
            }
          };
          // 鼠标松开事件
          document.onmouseup = function (evt) {
            mask.style.display = "none";
            mask1.style.display = "none";
            //颜色恢复
            resize[i].style.background = "#d6d6d6";
            document.onmousemove = null;
            document.onmouseup = null;
            resize[i].releaseCapture && resize[i].releaseCapture(); //当你不在需要继续获得鼠标消息就要应该调用ReleaseCapture()释放掉
          };
          resize[i].setCapture && resize[i].setCapture(); //该函数在属于当前线程的指定窗口里设置鼠标捕获
          return false;
        };
      }
    },
  },
  mounted() {
    this.dragControllerDiv();
    this.initGraph();
    // 禁用整个应用的默认右键菜单，但允许图形区域的右键事件
    document.addEventListener("contextmenu", this.handleGlobalContextMenu);
  },
  beforeDestroy() {
    // 移除事件监听
    document.removeEventListener("contextmenu", this.handleGlobalContextMenu);
  },
};
</script>

<style lang="less" scoped>
/deep/.x6-widget-selection-box {
  border: none;
  margin: 0;
  padding: 2px 0 0 2px;
  box-sizing: border-box;
  border-radius: 6px;
  box-shadow: 0 0 0 4px #e1efff;
}

// 结果抽屉样式
.result-drawer {
  position: absolute;
  bottom: 0;
  left: 200px;
  width: calc(100% - 200px);
  height: 200px;
  background-color: #fff;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
  z-index: 100;
  display: flex;
  flex-direction: column;
  
  .drawer-header {
    height: 40px;
    background-color: #f5f5f5;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 15px;
    border-bottom: 1px solid #e5e5e5;
    
    i {
      cursor: pointer;
      font-size: 16px;
      &:hover {
        color: #409EFF;
      }
    }
  }
  
  .drawer-resize-handle {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 5px;
    background-color: transparent;
    cursor: ns-resize;
    z-index: 101;
    
    &:hover {
      background-color: #e6e6e6;
    }
  }
  
  .drawer-content {
    flex: 1;
    padding: 15px;
    overflow-y: auto;
    
    pre {
      margin: 0;
      white-space: pre-wrap;
      word-break: break-all;
    }
    
    .no-result {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100%;
      color: #999;
    }
  }
}
#antvX6 {
  height: 100%;
  overflow: hidden;

  .content-header {
    padding: 0 20px 0 20px;
    width: calc(100% - 40px);
    height: 50px;
    line-height: 50px;
    background-color: #3c3c3c;

    .test {
      display: block !important;
    }

    .time-count {
      // padding-left: 25%;
      height: 100%;
      color: #ffffff;
    }
  }

  .content-main {
    height: calc(100% - 50px);
    display: flex;

    //左侧tab
    .content-left {
      position: relative;
      width: calc(450px - 10px);
      height: 100%;
      .header {
        height: 40px;
        line-height: 40px;
        background-color: #f5f5f5;
      }

      .mask-left {
        position: absolute;
        top: 0;
        left: 0;
        display: none;
        width: 100%;
        height: 100%;
        background-color: red;
        opacity: 0;
        z-index: 999;
      }
    }

    //拖拽区
    .resize {
      cursor: col-resize;
      float: left;
      position: relative;
      // top: 45%;
      background-color: #d6d6d6;
      // border-radius: 5px;
      // margin-top: -10px;
      width: 10px;
      height: 100%;
      background-size: cover;
      background-position: center;
      /*z-index: 99999;*/
      font-size: 32px;
      color: white;
    }

    .resize:hover {
      color: #444444;
    }

    //右侧虚拟机
    .content-right {
      position: relative;
      width: calc(100% - 450px);
      height: 100%;
      display: flex;
      .antv-menu {
        width: 200px;
        height: 100%;
        background-color: #fff;
        padding: 10px;
        box-sizing: border-box;
        overflow-y: auto;
        .category {
          border: 1px solid #e5e5e5;
          border-radius: 2px;
          &:not(:last-child) {
            margin-bottom: 10px;
          }
          .title {
            height: 30px;
            background-color: #e3e4e6;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 10px;
          }
          .cates-content {
            padding: 10px 15px;
            box-sizing: border-box;
          }
          .cate {
            position: relative;
            &:not(:last-child) {
              margin-bottom: 10px;
            }
            font-size: 14px;
            border: 1px solid #e5e5e5;
            border-radius: 5px;
            text-align: center;
            line-height: 30px;
            display: flex;
            align-items: center;
            justify-content: center;
            .icon {
              width: 30px;
              height: 100%;
              border-right: 1px solid #e5e5e5;
              background-color: #d3d3d3;
            }
            .name {
              flex: 1;
              height: 100%;
              background-color: #dedede;
            }
            .input,
            .output {
              width: 5px;
              height: 5px;
              border: 3px solid #f1f1f1;
              border-radius: 50%;
              background-color: #fff;
              position: absolute;
              top: 50%;
              transform: translateY(-50%);
            }
            .input {
              left: -5px;
            }
            .output {
              right: -5px;
            }
          }
        }
      }
      .antv-canvas {
        width: calc(100% - 200px) !important;
        height: 100%;
      }

      .mask-right {
        position: absolute;
        top: 0;
        left: 0;
        display: none;
        width: 100%;
        height: 100%;
        background-color: red;
        opacity: 0;
      }
    }
  }
}
</style>
